package auditoryUI;

import java.awt.Point;

import javax.swing.JOptionPane;

import auditoryUIbk.ContinuousControl;
import auditoryUIbk.Gesture;
import auditoryUIbk.SoundManager;
import auditoryUIbk.SoundSet;
import auditoryUIbk.TimedPolygon;

import experiment.ExperimentCondition;

public class SimpleDriver implements SimpleTouchpadInterface,
		SimpleWiTiltInterface {

	//	screen width and height
	public static final int DEBUG_WIDTH = 400; //1280;
	public static final int DEBUG_HEIGHT = 700; //1024;
	public static final int VF_WIDTH = 250; //visual frame width
	public static final int VF_HEIGHT = 320; //visual frame height

	public static final int DEBUG_RADIUS = DEBUG_WIDTH / 2 - 20;; //radius needs to be smaller than half of the shorter one of
	public static final double DEBUG_ZOOM = 0.5;

	float mfLastZoneIndex = 0f;
	private static int SPIRAL_ITEM_LIMIT = 12;
	//private static final int TAB_DISTANCE_LIMIT = 100;
	private static final int TAB_SIZE_LIMIT = 20;

	// ================================================
	// Variables to store play index, finger positions
	// These variable's value changes often
	// ================================================
	private static int miX, miY, miLastX, miLastY;

	private SimpleUI display;

	private Gesture moGesture;

	private SimpleStrokeAudioUI ta;

	private MenuHandler mh;

	private SimpleWiTiltAdapter sa;
	
	private ConfigurationChooser cc;
	
	private String condition = "";
	
	private SimpleExperimentManager em;
	
	private int prevSelection = -1;
	
	private boolean enabled = false;
	
	private int position;

	public SimpleDriver() {
		mh = new MenuHandler(this);
		display = new SimpleUI(this, 8);//dummy number
		display.repaint();
		ta = new SimpleStrokeAudioUI(this);
		//initialize tilt control
		
		//get user id
		//askUserID();
		cc = new ConfigurationChooser(this);
		cc.setLocation(display.getWidth() + 50, 100);
		em = new SimpleExperimentManager(this);
		em.setLocation(display.getWidth() + cc.getWidth() + 200, 100);
		sa = new SimpleWiTiltAdapter(this);
		
	}
	
	public void loadConfiguration(String file, String condition){
		this.condition = condition;
		mh.init(file);
		updateUI();
		enabled = true;
	}
	
	public String getConfiguration(){
		return condition;
	}

	public void touchpadButtonPressed(int button) {

	}
	
	public int getCurrentPosition(){
		return position;
	}
	
	public void setPosition(int p){
		position = p;
		//updateUI();
	}
	
	public boolean buttonEnabled(){
		String mode = display.getCurrrentInputMode();
		if (mode.equals("Button")){
			return true;
		}
		return false;
	}
	
	public boolean gastureEnabled(){
		String mode = display.getCurrrentInputMode();
		if (mode.equals("Gasture")){
			return true;
		}
		return false;
	}
	
	public boolean tiltEnabled(){
		String mode = display.getCurrrentInputMode();
		if (mode.equals("Tilt")){
			return true;
		}
		return false;
	}

	public void touchpadButtonReleased(int button) {
		if (!enabled){
			return;
		}
		if (buttonEnabled()){
			// enter next level
			if (button == 2) {
				int n = mh.getSeletectedItemIndex();
				mh.nextLevel(n);
			} else if (button == 1) {
				mh.prevLevel();
			}
			em.logUserAction("Button pressed", getCurrentItemName(), miX, miY, 0);
			updateUI();
		}
	}

	public void fingerTouchedCircle(int x, int y, int button, int z) {
		if (!enabled){
			return;
		}
		if (em.getStatus() == Constants.STATUS_STANDBY 
				|| em.getStatus() == Constants.STATUS_END){
			em.userStart();
		}

		miX = x;
		miY = y;
		moGesture = new Gesture(100);
		moGesture.addPoint(x, y, z);
		// 4. compute and handle the zone information here
		// only if we are at the browsing modes
		mfLastZoneIndex = computeAndHandleZoneForCurrentItem(x, y, false,
				SPIRAL_ITEM_LIMIT); // item selected is false here
		// 3.5 handle the spiral zone case
		//handleSpiralZone(x, y);

		// 6. paint and set the paint status again
		//PaintStatus = PAINT_STROKE_COMPONENT;
		// 5. record a set of values
		miLastX = miX;
		miLastY = miY;
		//em.logUserAction("Touched", getTreeIndex(),getCurrentItemName(), x, y, z);
		updateUI();
	}

	public void fingerDraggedCircle(int x, int y, int button, int z, int x0,
			int y0) {
		if (!enabled){
			return;
		}
		if (tiltEnabled() && (em.getStatus() == Constants.STATUS_STANDBY 
				|| em.getStatus() == Constants.STATUS_END)){
			em.userStart();
		}
		miX = x;
		miY = y;
		moGesture.addPoint(x, y, z);

		if (moGesture.size() < TAB_SIZE_LIMIT){
			return;
		}
		//handleSpiralZone(x, y);
		mfLastZoneIndex = computeAndHandleZoneForCurrentItem(x, y, false,
				SPIRAL_ITEM_LIMIT);

		miLastX = miX;
		miLastY = miY;
		if (hasIndexChanged()){
			//em.logUserAction("Dragged", getTreeIndex(), getCurrentItemName(),x, y, z);
		}
		updateUI();
	}// end of finger dragged

	/**
	 * finger release from the touchpad
	 * 
	 * @param x
	 * @param y
	 * @param button
	 */
	public void fingerReleasedCircle(int x, int y, int button, int z, int x0,
			int y0) {
		if (!enabled){
			return;
		}

		// 2. actually, because the shape of the finger, when release, the
		// contact point
		// often changes which causes unnecessary error. To make the selection
		// more robust
		// we remove the last three contact points from the gesture, and take
		// the 4th last point
		int jitter_size = 4;

		//1. finish the gesture point
		moGesture.addPoint(x, y, z);

		if (moGesture.size() > jitter_size) {
			TimedPolygon points = moGesture.getPoints();
			int[] xPoints = points.xpoints;
			int[] yPoints = points.ypoints;
			int length = xPoints.length;
			x = xPoints[length - jitter_size];
			y = yPoints[length - jitter_size];
		}

		// 2. set the coordinates
		miX = x;
		miY = y;

		// double distance = ContinuousControl.getLength(new
		// Point(moGesture.lastx, moGesture.lasty), new Point(tPoly.xpoints[0],
		// tPoly.ypoints[0]));
		// debug("distance is "+ distance + " size is " + moGesture.size());

		// boolean userTapped = false;

		if (moGesture.size() < TAB_SIZE_LIMIT) {
			if (isWithinCenterCycle(x, y)) {
				mh.prevLevel();
				updateUI();
			}
			fingerTappedCircle(x, y, button, z);
			return;
		} else if (isWithinCenterCycle(x, y)&& gastureEnabled()) {
			mh.prevLevel();
			updateUI();
		}

		mfLastZoneIndex = computeAndHandleZoneForCurrentItem(x, y, true,
				SPIRAL_ITEM_LIMIT);

		moGesture = null;

		miLastX = miX;
		miLastY = miY;
		//em.logUserAction("Released", getTreeIndex(), getCurrentItemName(),x, y, z);
		updateUI();
	}

	/**
	 * This event is a compound event taken from a compound event
	 * 
	 * @param x
	 * @param y
	 * @param button
	 * @param z
	 */
	public void fingerTappedCircle(int x, int y, int button, int z) {
		if (!enabled || tiltEnabled()){
			return;
		}
		mh.setCoordinates(x, y);
		int n = mh.getSeletectedItemIndex();
		if (n != -1 && !isWithinCenterCycle(x, y) && gastureEnabled()) {
			//em.logUserAction("Tapped", getTreeIndex(), getCurrentItemName(),x, y, z);
			if (!mh.nextLevel(n)){
				em.hasReachedGoal(getCurrentItemName());
			}
			updateUI();
		}
	}

	private boolean isWithinCenterCycle(int x, int y) {
		double dis = (Math.pow((x - Constants.TP_CENX), 2) + Math.pow(
				Constants.TP_CENY - y, 2));
		if (dis <= 45000) {
			return true;
		}
		return false;
	}

	public void tiltUp() {
		if (!tiltEnabled()){
			return;
		}
		mh.prevLevel();
		updateUI();
	}

	public void tiltDown() {
		if (!tiltEnabled()){
			return;
		}
		int n = mh.getSeletectedItemIndex();
		if (!mh.nextLevel(n)){
			em.hasReachedGoal(getCurrentItemName());
		}
		updateUI();
	}
	
	public void reset(){
		mh.reset();
		updateUI();
	}

	public SimpleUI getDisplay(){
		return display;
	}
	
	public String getStatus(){
		return "Trial start";
	}
	/**
	 * Compute and the handle the zones for the current selected item A method
	 * used to handle situation in browsing command mode For expert command
	 * mode, we use either multi-stroke marking menu or some other technique.
	 * 
	 * There is a problem for computeAndHandleZone here for the dynamic shifting
	 * zone compact mode. When we going from the end of the circle to the
	 * beginning again, or from the beginning to the end, the computation of the
	 * zones will be going back and forth. This is due to the incompatibility
	 * between the circular continuous nature of the touchpad, and the linear
	 * nature of the algorithm used to determine the zones. Earlier, if the
	 * finger is at a zone close to the border between the beginning and the
	 * end, I will assign the weights to the other end of the circle for
	 * example, if the finger is at zone 0, the window covers -2, -1, 0, 1, 2, I
	 * will assign the new weight to length - index, so if the index is -2,
	 * length is 41, the new weight will be assigned to slot 39, however, this
	 * cause a shift problem in calculation of the zones in getZoneWeightedN
	 * since it's taking the cumulative of the zoneWeight/totalweight to adjust
	 * the angles. change the sequence like that causes a nonlinear jump to a
	 * linear in nature shift.
	 * 
	 * To solve this problem, i will increase the weight size by the size of the
	 * window, and set the first
	 * 
	 * @param x
	 * @param y
	 * @param itemSelected
	 * @param itemLimit
	 * @return
	 */
	public float computeAndHandleZoneForCurrentItem(int x, int y,
			boolean itemSelected, int itemLimit) {

		// 4. get the zone information
		int numOfItemsThisLevel = 0;
		//		if(moLastItem == null){
		//			// we are at the root
		//			numOfItemsThisLevel = moGraphicModel.getLayer(0).size();
		//		}
		//		else{
		//			// check the leaf level
		//			numOfItemsThisLevel = moLastItem.getChildCount();
		//		}

		// 5. get the zone index for
		float zoneIndex = 0F;
		// if it's spiral mode and number of items is greater than 8, then
		// we use spiral mode
		//handleBrowsingCommands(Math.round(zoneIndex), itemSelected);
		return zoneIndex;
	}

	public static void main(String[] args) {
		SimpleDriver sd = new SimpleDriver();
	}

	public void onExit() {
		//sa.onExit();
		ta.terminateTouchPad();
		//sa.();
		System.exit(0);
	}
	
	private String getCurrentItemName(){
		int n = mh.getSeletectedItemIndex();
		return mh.getItemText(n);
		
	}

	/**
	 * This function updates the gui part of the program
	 */
	public void updateUI() {
		mh.setCoordinates(miX, miY);
		int n = mh.getSeletectedItemIndex();
		display.setWeights(mh.getCurrentItemWeight());
		display.updateUI(miX, miY, n, mh.getNumMenuAtCurrentLevel(), mh
				.getLevel());
		mh.playSound(n);
		//String name = mh.getItemText(n);
		//display.showMessage(name);
	}
	
	public void updateMenu(){
		mh.updateMenu();
		updateUI();
	}
	
	private boolean hasIndexChanged(){
		int index = mh.getSeletectedItemIndex();
		if (prevSelection == -1||prevSelection != index){
			prevSelection = index;
			return true;
		}
		else if (prevSelection == index){
			return false;
		}
		return false;
	}

}
